package br.com.maikosoft.mklib;


import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/**
 * A JTextField that will display a whole word as the user types a portion of
 * it. A better example would be, as the user types the first letter in the word
 * "text" the AutoCompleteTextField will display the letter "t" in the
 * components current foreground color and display the rest of the word "ext" in
 * the specified incomplete color (default is Color.GRAY.brighter()). The
 * displayed text changes as the user types "e" so now getText() would return
 * the string "te" and the incomplete portion displayed by the editor will be
 * "xt." If Enter is pressed while an incomplete word is being processes, then
 * the AutoCompleteTextField will replace what he user has typed with the
 * completed word and consider itself "finished," and if Enter is pressed a
 * second time, then it will fire a KeyEvent normally.
 *
 * @author Brandon Buck
 * @version 1.0
 */
public class MkFieldTextComplete extends JTextField implements
        KeyListener, FocusListener, DocumentListener {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private ArrayList<String> possibilities;
    private int currentGuess;
    private Color incompleteColor;
    private boolean areGuessing;
    private boolean caseSensitive;

    /**
     * Constructs a new AutoCompleteTextField with the 5 columns by default and
     * no case sensitivity on comparisons of guesses to entered text.
     */
    public MkFieldTextComplete() {
        this(5, false);
    }

    /**
     * Constructs a new AutoCompleteTextField with the specified number of
     * columns with no case sensitivity when comparing the current guess to the
     * text entered.
     *
     * @param columns
     *            The number of columns you wish for the width of this
     *            AutoCompleteTextField.
     */
    public MkFieldTextComplete(int columns) {
        this(columns, false);
    }

    /**
     * Creates a new AutoCompleteTextField with the given number of columns in
     * width and the setting for case sensitivity when comparing the current
     * guess to the entered text.
     *
     * @param columns
     *            The number of columns of text for this component.
     * @param caseSensitive
     *            <code>true</code> or <code>false</code> depending on if you
     *            want comparisons to be case sensitive or not.
     */
    public MkFieldTextComplete(int columns, boolean caseSensitive) {
        super.setColumns(columns);
        this.possibilities = new ArrayList<String>();
        this.incompleteColor = Color.GRAY.brighter();
        this.currentGuess = -1;
        this.areGuessing = false;
        this.caseSensitive = caseSensitive;
        this.addKeyListener(this);
        this.addFocusListener(this);
        this.getDocument().addDocumentListener(this);
    }

    /**
     * Add a new possibility to the list of possibilities. Add a new possibility
     * to the list of possibilities for the AutoCommpleteTextField to process.
     *
     * @param possibility
     *            The new possibility to add.
     */
    public void addPossibility(String possibility) {
        this.possibilities.add(possibility);
        Collections.sort(possibilities);
    }
    /**
     * Add a new possibility.s to the list of possibilities. Add a new possibility
     * to the list of possibilities for the AutoCommpleteTextField to process.
     *
     * @param possibility
     *            More than one parameters
     */
    public void addPossibility(String... possibility) {
        for (String possible : possibility) {           
            addPossibility(possible);
        }
    }
   
    /**
     * Add a new list of the possibility.s to the list of possibilities. Add a new possibility
     * to the list of possibilities for the AutoCommpleteTextField to process.
     *
     * @param possibility
     *            More than one parameters
     */
    public void addPossibility(List<String> possibility) {
        for (String possible : possibility) {           
            addPossibility(possible);
        }
    }

    /**
     * Removes a possibility from the list of possibilities. Removes the given
     * possibility from the list of possibilities so that it will no longer be
     * matched.
     *
     * @param possibility
     *            The possibility to remove.
     */
    public void removePossibility(String possibility) {
        this.possibilities.remove(possibility);
    }

    /**
     * Removes all possibilities in the list. Removes every possibility in the
     * list and starts over with an empty list ready for new possibilities to be
     * added.
     */
    public void removeAllPossibilities() {
        this.possibilities = new ArrayList<String>();
    }

    /**
     * Sets the color to draw the incomplete guess in. This sets the color that
     * the incomplete guess text is drawn in.
     *
     * @param incompleteColor
     *            The new color to draw the incomplete guess with.
     */
    public void setIncompleteColor(Color incompleteColor) {
        this.incompleteColor = incompleteColor;
    }

    /**
     * Returns the current guess from the list of possibilities. Returns the
     * string at the location of the current guess in the list of possibilities.
     *
     * @return The current guess as a String.
     */
    private String getCurrentGuess() {
        if (this.currentGuess != -1)
            return this.possibilities.get(this.currentGuess);

        return this.getText();
    }

    /**
     * Changes the current case sensitive setting to the given setting.
     *
     * @param caseSensitive
     *            <code>true</code> or <code>false</code> depending on if you
     *            want comparisons to be case sensitive or not.
     */
    public void setCaseSensitive(boolean caseSensitive) {
        this.caseSensitive = caseSensitive;
    }

    private void findCurrentGuess() {
        String entered = this.getText();
        if (!this.caseSensitive)
            entered = entered.toLowerCase();

        for (int i = 0; i < this.possibilities.size(); i++) {
            currentGuess = -1;

            String possibility = this.possibilities.get(i);
            if (!this.caseSensitive)
                possibility = possibility.toLowerCase();
            if (possibility.startsWith(entered)) {
                this.currentGuess = i;
                break;
            }
        }
    }

    @Override
    public void setText(String text) {
        super.setText(text);
        this.areGuessing = false;
        this.currentGuess = -1;
    }

    @Override
    public void paintComponent(Graphics g) {
        String guess = this.getCurrentGuess();
        String drawGuess = guess;

        super.paintComponent(g);
        String entered = this.getText();
        Rectangle2D enteredBounds = g.getFontMetrics().getStringBounds(entered,
                g);

        if (!(this.caseSensitive)) {
            entered = entered.toLowerCase();
            guess = guess.toLowerCase();
        }

        if (!(guess.startsWith(entered)))
            this.areGuessing = false;

        if (entered != null && !(entered.equals("")) && this.areGuessing) {
            String subGuess = drawGuess.substring(entered.length(),
                    drawGuess.length());
            Rectangle2D subGuessBounds = g.getFontMetrics().getStringBounds(
                    drawGuess, g);

            int centeredY = ((getHeight() / 2) + (int) (subGuessBounds
                    .getHeight() / 2));

            g.setColor(this.incompleteColor);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(subGuess);
            // stringBuilder.append("   press ENTER to send or \u2192 to fill");
            g.drawString(stringBuilder.toString(),
                    (int) (enteredBounds.getWidth()) + 2, centeredY - 2);
        }
    }

    public void keyTyped(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            if (this.areGuessing) {
                this.setText(this.getCurrentGuess());
                this.areGuessing = false;
            }
        }
       
        if ((e.getKeyCode() == KeyEvent.VK_BACK_SPACE) && ((e.isControlDown()))) {
            if (this.areGuessing) {
                this.setText(this.getText());
                this.areGuessing = false;
            }
            this.possibilities.clear();
        }
    }

    public void keyReleased(KeyEvent e) {
    }

    public void insertUpdate(DocumentEvent e) {
        String temp = this.getText();

        if (temp.length() == 1)
            this.areGuessing = true;

        if (this.areGuessing)
            this.findCurrentGuess();

    }

    public void removeUpdate(DocumentEvent e) {
        String temp = this.getText();

        if (!(this.areGuessing))
            this.areGuessing = true;

        if (temp.length() == 0)
            this.areGuessing = false;
        else if (this.areGuessing) {
            this.findCurrentGuess();
        }
    }

    public void changedUpdate(DocumentEvent e) {
    }

    public void focusGained(FocusEvent arg0) {
    	
    }

    public void focusLost(FocusEvent arg0) {
        if (this.areGuessing) {
            this.setText(this.getText());
            this.areGuessing = false;
        }
    }
   
    public String getPossibity(){
        if (this.currentGuess != -1)
            return this.possibilities.get(this.currentGuess);

        return "";
    }
}